/*
 * shawnbgu_local_topology.cpp
 *
 *  Created on: Apr 25, 2009
 *      Author: gal
 */


#include "shawnbgu_local_topology.h"

namespace shawnbgu{

// ----------------------------------------------------------------------

ShawnbguLocalTopology::ShawnbguLocalTopology()
{}


// ----------------------------------------------------------------------

ShawnbguLocalTopology::ShawnbguLocalTopology(int owner_id, int radius)
{
	owner_id_=owner_id;
	radius_=radius;
	neighbourhood_data_=ShawnbguNeighbourhoodData();
	}

// ----------------------------------------------------------------------

ShawnbguLocalTopology::	ShawnbguLocalTopology(const ShawnbguLocalTopology& other)
{
	owner_id_= other.owner_id_;
	neighbourhood_data_=ShawnbguNeighbourhoodData(other.neighbourhood_data_);
	}

// ----------------------------------------------------------------------




ShawnbguLocalTopology::~ShawnbguLocalTopology()
{}

// ----------------------------------------------------------------------
/*
ShawnbguLocalTopology::ShawnbguLocalTopology(const ShawnbguLocalTopology& other) :
	neighbourhood_data(other.neighbourhood_data), owner_id_(other.owner_id_)
	{}
*/

// ----------------------------------------------------------------------
void
ShawnbguLocalTopology::init(int radius, int owner_id,bool is_leader, int leadership)
{
	DEBUG( logger(), "node No. "
			  <<owner_id
			  <<" entering ShawnbguLocalTopology::init");
	//TODO init radius and owner id just once.
	radius_=radius;
	owner_id_=owner_id;
	neighbourhood_data_ = ShawnbguNeighbourhoodData();
	//TODO convert shawnbguNodeData into a struct
	owner_data_.node_id_=owner_id;
	owner_data_.parent_id_=owner_id;
	owner_data_.is_leader_=is_leader;
	owner_data_.leadership_=leadership;
	if (neighbourhood_data_.insert(0,owner_id, owner_data_))
		{
		DEBUG( logger(), "node No. "
				<<owner_id
					  	  <<" is inserted to node's No. "
					  	  << owner_id
					  	  <<" local topology in distance No. "
					  	  << 0 );


		}

}
// ----------------------------------------------------------------------
int
ShawnbguLocalTopology::owner_id_w()
{


	return owner_id_;


}
// ----------------------------------------------------------------------
map<int,shawnbguNodeData>&
ShawnbguLocalTopology::nodes_in_dist(int dist)
{

	return neighbourhood_data_.nodes_in_dist(dist);

}


// ----------------------------------------------------------------------
bool
ShawnbguLocalTopology::nodes_in_dist_exist(int dist)
{

	return neighbourhood_data_.nodes_in_dist_exist(dist);

}

// ----------------------------------------------------------------------

//given ShawnbguLocalTopology neighbour_topology
//add the neighbour's knowledge to this neighbourhood_data.
void
ShawnbguLocalTopology::
add(ShawnbguLocalTopology neighbour_topology)
{
	DEBUG( logger(), "node No. "
					  <<owner_id_
					  <<" entering ShawnbguLocalTopology::add");

	for (int dist = 0; dist < radius_; dist++)
	{
		if (neighbour_topology.nodes_in_dist_exist(dist)) {

		DEBUG( logger(), "node No. "
					<<neighbour_topology.owner_id_w()
					<<" has nodes in distance "
					<< dist
			);



			map<int, shawnbguNodeData> nodes =
					neighbour_topology.nodes_in_dist(dist);
			int parent_id = neighbour_topology.owner_id_w();
			int length =nodes.size();
			map<int, shawnbguNodeData>::iterator nodes_it;
			for (nodes_it = nodes.begin(); nodes_it != nodes.end(); nodes_it++) {
				//shawnbguNodeData new_data = shawnbguNodeData();
				shawnbguNodeData new_data = nodes_it->second;
				new_data.node_id_=nodes_it->second.node_id_;
				new_data.parent_id_= parent_id;
				if (neighbourhood_data_.insert(dist + 1, nodes_it->first,
						new_data)) {

					DEBUG( logger(), "node No. "
							<<owner_id_w()
							<<" inserted node "
							<<nodes_it->first
							<< " in distance "
							<< dist+1
							<<" with parent id "
							<<parent_id
					);



				}
				else{

					DEBUG( logger(), "node No. "
							<<owner_id_w()
							<<" DID NOT insert node "
							<<nodes_it->first
							<< " in distance "
							<< dist+1
					);

				}

			}

		}



		else{

			DEBUG( logger(), "node No. "
					<<neighbour_topology.owner_id_w()
					<<" does not have nodes in distance "
					<< dist
			);

			break;
		}
	}

}
// ----------------------------------------------------------------------
//TODO think maybe to return nodeData
pair <int,int>
ShawnbguLocalTopology::
find_leader()
{
	DEBUG( logger(), "node No. "
		<<owner_id_
		<<" entering ShawnbguLocalTopology::add");





		queue<shawnbguNodeData> leaders = find_leaders();
		if (leaders.size() > 0) {
		while (leaders.size() > 0) {

			shawnbguNodeData other_leader = leaders.front();

			if (owner_data_.is_leader_) {

				if (owner_data_.leadership_ < other_leader.leadership_) {
					update_leader(other_leader);
					break;
				}
			} else { //this is not leader
				update_leader(other_leader);
				break;
			}
			leaders.pop();
		}
	}
	//no other leader
	else {
		if (!owner_data_.is_leader_) {
			update_leader(owner_data_);
		}

	}


	DEBUG( logger(), "node No. "
			<<owner_id_w()
			<<" leader is  "
			<< owner_data_.leader_id_
	);



//return make_pair(owner_data_.is_leader_,owner_data_.leadership_);
	return make_pair(owner_data_.leader_id_,owner_data_.leadership_);


}


// ----------------------------------------------------------------------

queue<shawnbguNodeData> ShawnbguLocalTopology::find_leaders() {
	queue<shawnbguNodeData> leaders;
	for (int dist = 1; dist < radius_; dist++) {
		if (neighbourhood_data_.nodes_in_dist_exist(dist)) {

			map<int, shawnbguNodeData> nodes =
					neighbourhood_data_.nodes_in_dist(dist);
			for (map<int, shawnbguNodeData>::iterator nodes_it = nodes.begin(); nodes_it
					!= nodes.end(); nodes_it++) {

				shawnbguNodeData node_data = nodes_it->second;
				if (node_data.is_leader_) {
					leaders.push(node_data);
				}
			}

		}
	}
	return leaders;

}

// ----------------------------------------------------------------------
void
ShawnbguLocalTopology::
update_leader(shawnbguNodeData leader)
{
	if (owner_data_.node_id_==leader.node_id_){
		owner_data_.is_leader_=true;
	}
	else{
		owner_data_.is_leader_=false;
	}

	owner_data_.leader_id_ = leader.node_id_;

	//neighbourhood_data_.remove_old_value(0,owner_data_.node_id_);
	neighbourhood_data_.insert(0,owner_data_.node_id_,owner_data_);

}

// ----------------------------------------------------------------------

}
